﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using TestOnlineProject.Models.Entities;
using TestOnlineProject.Common;
using TestOnlineProject.Models.DataAccess;
using System.Web;

namespace TestOnlineProject.Models.BusinessLogic
{
    public class SubjectBLO
    {
        SubjectDAO subjectDAO = new SubjectDAO();
        ParticipantDAO participantDAO = new ParticipantDAO();
        UserTestDAO userTestDAO = new UserTestDAO();

        /// <summary>
        /// Select subject list.
        /// </summary>
        /// <param name="page">Number of pages</param>
        /// <param name="keyWord">Search key word</param>
        /// <param name="column">Column name</param>
        /// <param name="sort">Sort type</param>
        /// <returns>Subject list</returns>

        public List<Subject> ToList(ref List<NoModel> noList, int? page, string keyWord = "", string column = "", string sort = "")
        {
            try
            {
                // Subject list.
                var subjects = subjectDAO.Select();

                if (HttpContext.Current.User.IsInRole("2") || HttpContext.Current.User.IsInRole("3"))
                {
                    subjects = subjects.Where(s => s.IsActivated == true).ToList();
                }

                // Search by keyword.
                if (keyWord.Equals(""))
                {
                    subjects = subjects.OrderBy(s => s.SubjectId).ToList();
                }
                else
                {
                    subjects = subjects.Where(s => (s.Name.ToLower().Contains(keyWord.ToLower()) || s.Code.ToLower().Contains(keyWord.ToLower()))).OrderBy(u => u.SubjectId).ToList();
                }

                // Sort.
                switch (column + sort)
                {
                    case "NoAsc":
                        subjects = subjects.OrderBy(u => u.SubjectId).ToList();
                        break;
                    case "NoDesc":
                        subjects = subjects.OrderByDescending(u => u.SubjectId).ToList();
                        break;
                    case "CodeAsc":
                        subjects = subjects.OrderBy(u => u.Code).ToList();
                        break;
                    case "CodeDesc":
                        subjects = subjects.OrderByDescending(u => u.Code).ToList();
                        break;
                    case "NameAsc":
                        subjects = subjects.OrderBy(u => u.Name).ToList();
                        break;
                    case "NameDesc":
                        subjects = subjects.OrderByDescending(u => u.Name).ToList();
                        break;
                    case "IsActivatedAsc":
                        subjects = subjects.OrderBy(u => u.IsActivated).ToList();
                        break;
                    case "IsActivatedDesc":
                        subjects = subjects.OrderByDescending(u => u.IsActivated).ToList();
                        break;
                    default: break;
                }

                // Generate no. List.
                foreach (var v in subjects)
                {
                    noList.Add(new NoModel() { Id = v.SubjectId });
                }
                noList = noList.OrderBy(s => s.Id).ToList();
                for (int i = 0; i < noList.Count; i++)
                {
                    noList[i].No = i + 1;
                }

                return subjects;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>

        //public List<Subject> ToList(string keyWord = "")
        //{
        //    var subjects = subjectDAO.Select().Where(s => s.IsActivated == true && (s.Name.ToLower().Contains(keyWord.ToLower()) || s.Code.ToLower().Contains(keyWord.ToLower()))).ToList();
        //    return subjects;
        //}

        public List<Subject> ToList()
        {
            try
            {
                var subjects = subjectDAO.Select();
                return subjects;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public List<Subject> Recommend()
        {
            try
            {
                // Recommend list.
                var subjects = new List<Subject>();

                if (!HttpContext.Current.User.IsInRole("1"))
                {
                    // All subject that user joined.
                    var subjectIds = participantDAO.SelectByUserId(int.Parse(HttpContext.Current.User.Identity.Name)).Where(p => p.Subject.IsActivated && p.User.IsActivated && p.IsActivated).Select(p => p.SubjectId).ToArray();

                    // All user tests.
                    var userTests = userTestDAO.SelectByUserId(int.Parse(HttpContext.Current.User.Identity.Name)).OrderByDescending(ut => ut.UserTestId).ToList();

                    // If user already take the tests.
                    if (userTests.Count > 0)
                    {
                        foreach (var ut in userTests)
                        {
                            // If testtype is classic test.
                            if (ut.TestId != null)
                            {
                                if (subjectIds.Contains(ut.Test.SubjectId))
                                {
                                    // If subjectId not exist in list.
                                    if (subjects.Count < 5 && !subjects.Select(s => s.SubjectId).ToArray().Contains(ut.Test.SubjectId))
                                    {
                                        subjects.Add(subjectDAO.SelectBySubjectId(ut.Test.SubjectId));
                                    }
                                }
                            }
                            // If test type is quick test.
                            else
                            {
                                if (ut.UserAnswers.Count > 0)
                                {
                                    if (subjectIds.Contains(ut.UserAnswers.ToList()[0].Question.SubjectId))
                                    {
                                        // If subjectId not exist in list.
                                        if (subjects.Count < 5 && !subjects.Select(s => s.SubjectId).ToArray().Contains(ut.UserAnswers.ToList()[0].Question.SubjectId))
                                        {
                                            subjects.Add(subjectDAO.SelectBySubjectId(ut.UserAnswers.ToList()[0].Question.SubjectId));
                                        }
                                    }
                                }
                            }
                        }
                    }
                    // If user is new, get all subject that user joined.
                    //else
                    //{
                    //    subjectIds = subjectIds.OrderByDescending(s => s).ToArray();
                    //    for (int i = 0; i < subjectIds.Length; i++)
                    //    {
                    //        if (subjects.Count < 5)
                    //        {
                    //            subjects.Add(subjectDAO.SelectBySubjectId(subjectIds[i]));
                    //        }
                    //    }
                    //}
                }
                else
                {
                    //+++
                    subjects = subjectDAO.Select().Where(s => s.IsActivated).ToList();
                }

                return subjects;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Add subject.
        /// </summary>
        /// <param name="subject">Subject object</param>
        /// <returns>Number of subject has been add</returns>

        public int Add(Subject subject)
        {
            try
            {
                // Set default values.
                subject.IsActivated = true;
                int result = 0;

                if (!CheckDuplicateSubjectCode(subject.Code))
                {
                    // Add subject.
                    result = subjectDAO.Insert(subject);
                }
                else
                {
                    // Duplicate code.
                    result = -1;
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        private bool CheckDuplicateSubjectCode(string code, int subjectid = 0)
        {
            try
            {
                var subject = subjectDAO.SelectByCode(code);

                if (subjectid > 0)
                {
                    if (subject != null && subject.SubjectId != subjectid)
                    {
                        return true;
                    }
                }
                else
                {
                    if (subject != null)
                    {
                        return true;
                    }
                }
                return false;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Find subject by id.
        /// </summary>
        /// <param name="id">Subject id</param>
        /// <returns>Subject object</returns>
        /// 

        public Subject Find(int id)
        {
            try
            {
                // Select object by id.
                var subject = subjectDAO.SelectBySubjectId(id);

                return subject;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public bool CheckAccessible(int userId = 0, int subjectId = 0)
        {
            try
            {
                if (HttpContext.Current.User.IsInRole("1"))
                {
                    return true;
                }

                var participant = participantDAO.SelectByUserIdSubjectId(userId, subjectId);
                if (participant != null)
                {
                    if (participant.IsActivated && participant.Subject.IsActivated && participant.User.IsActivated)
                    {
                        return true;
                    }
                }

                return false;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Deactivate subjects.
        /// </summary>
        /// <param name="ids">Subject ids string</param>
        /// <returns>Number of subjects has been deactivated</returns>

        public int Deactivate(string ids = "")
        {
            try
            {
                StringHelper spliter = new StringHelper();

                // Split id string to int array.
                int[] idInt = spliter.Split(ids);

                int result = 0;

                // Deactivate subject.
                foreach (var i in idInt)
                {
                    // Select subject by id.
                    var subject = subjectDAO.SelectBySubjectId(i);

                    // Check subject is activated?
                    if (subject.IsActivated)
                    {
                        subject.IsActivated = false;
                        result += subjectDAO.Update(subject);
                    }
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Activate subject.
        /// </summary>
        /// <param name="ids">Subject ids string</param>
        /// <returns>Number of subjects has been activate</returns>

        public int Activate(string ids = "")
        {
            try
            {
                StringHelper spliter = new StringHelper();

                // Split id string to int array.
                int[] idInt = spliter.Split(ids);

                int result = 0;

                // Activate subjects.
                foreach (var i in idInt)
                {
                    // Select subject by id.
                    var subject = subjectDAO.SelectBySubjectId(i);

                    // Check subject is deactivated?
                    if (!subject.IsActivated)
                    {
                        subject.IsActivated = true;
                        result += subjectDAO.Update(subject);
                    }
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public int Remove(string ids = "")
        {
            try
            {
                StringHelper spliter = new StringHelper();

                // Split id string to int array.
                int[] idInt = spliter.Split(ids);

                int result = 0;

                // Delete subjects.
                foreach (var i in idInt)
                {
                    // Check subject is deactivated?
                    result += subjectDAO.Delete(i);
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Modify subject.
        /// </summary>
        /// <param name="subject">Subject id</param>
        /// <returns>Numner of subject has been modified</returns>

        public int Modify(Subject subject)
        {
            try
            {
                int result = 0;

                // Select aubject by id.
                var sub = subjectDAO.SelectBySubjectId(subject.SubjectId);

                if (!CheckDuplicateSubjectCode(subject.Code, subject.SubjectId))
                {
                    // Modify subject.
                    sub.Code = subject.Code;
                    sub.Name = subject.Name;
                    result = subjectDAO.Update(sub);
                }
                else
                {
                    // Duplicate code.
                    result = -1;
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Import subject
        /// </summary>
        /// <param name="conection"></param>
        /// <param name="filepath"></param>
        /// <returns></returns>
        public Tuple<List<SubjectErrorModel>, int, int, bool, bool> ParseFile(string conection, string filepath)
        {
            var listSubject = new List<SubjectErrorModel>();
            bool fatalError = false;
            int countSuccess = 0;
            int countFail = 0;
            var time = DateTime.Now.AddSeconds(90);
            bool timeout = false;

            try
            {
                // Fill the dataset
                var adapter = new OleDbDataAdapter("SELECT * FROM [Sheet1$]", conection);
                var ds = new DataSet();
                adapter.Fill(ds, "result");
                DataTable data = ds.Tables["result"];

                // check exist columns
                if (!data.Columns.Contains("Code"))
                {
                    fatalError = true;
                }
                else if (!data.Columns.Contains("Name"))
                {
                    fatalError = true;
                }
                else
                {
                    for (int i = 0; i <= data.Rows.Count - 1; i++)
                    {
                        // check timeout
                        if (DateTime.Now > time)
                        {
                            timeout = true;
                            break;
                        }

                        // get data from excel
                        var subjectError = new SubjectErrorModel();
                        subjectError.Code = data.Rows[i].Field<string>("Code");
                        subjectError.Name = data.Rows[i].Field<string>("Name");

                        // check value in column
                        if (String.IsNullOrEmpty(subjectError.Code))
                        {
                            countFail++;
                            subjectError.Error = "Code of subject is null or empty.";
                            listSubject.Add(subjectError);
                        }
                        else if (String.IsNullOrEmpty(subjectError.Name))
                        {
                            countFail++;
                            subjectError.Error = "Subject's Name is null or empty.";
                            listSubject.Add(subjectError);
                        }
                        else if (subjectDAO.SelectByName(subjectError.Name) != null)
                        {
                            countFail++;
                            subjectError.Error = "This subject's name is duplicated.";
                            listSubject.Add(subjectError);
                        }
                        else if (subjectDAO.SelectByCode(subjectError.Code) != null)
                        {
                            countFail++;
                            subjectError.Error = "This code is duplicated.";
                            listSubject.Add(subjectError);
                        }
                        else if (subjectError.Code.Length > 10 || subjectError.Code.Length < 2)
                        {
                            countFail++;
                            subjectError.Error = "Code of subject is 2 - 10 characters.";
                            listSubject.Add(subjectError);
                        }
                        else if (subjectError.Code.Contains(" "))
                        {
                            countFail++;
                            subjectError.Error = "Code of subject can't contain whitespaces";
                            listSubject.Add(subjectError);
                        }
                        else if (subjectError.Name.Length > 100 || subjectError.Name.Length < 3)
                        {
                            countFail++;
                            subjectError.Error = "Subject's name has lenght is 3 - 100 characters.";
                            listSubject.Add(subjectError);
                        }
                        else
                        {
                            // add value to object
                            var subject = new Subject();

                            subject.IsActivated = true;

                            subject.Code = data.Rows[i].Field<string>("Code");
                            subject.Name = data.Rows[i].Field<string>("Name");
                            int result = subjectDAO.Insert(subject);
                            if (result == 0)
                            {
                                countFail++;
                                subjectError.Error = "Can't import this subject.";
                                listSubject.Add(subjectError);
                            }
                            else
                            {
                                listSubject.Add(subjectError);
                                countSuccess++;
                            }
                        }
                    }
                }
                File.Delete(filepath);
                return new Tuple<List<SubjectErrorModel>, int, int, bool, bool>(listSubject, countSuccess, countFail, fatalError, timeout);
            }
            catch (Exception)
            {
                File.Delete(filepath);
                return new Tuple<List<SubjectErrorModel>, int, int, bool, bool>(null, 0, 0, true, false);
            }

        }
    }
}